package com.glasgowrax.util;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import com.glasgowrax.domain.Feature;
import com.glasgowrax.domain.LatLng;
import com.glasgowrax.util.infrastructure.FeatureListTooSmallException;

public class LocationUtil {

	public static List<Feature> getNearestFeatureToTarget(int number,
			List<Feature> features, LatLng target) throws FeatureListTooSmallException {
		Map<Feature, Double> featureMap = findDistances(features, target);
		ValueComparator comparator = new ValueComparator(featureMap);
		TreeMap<Feature, Double> sortedFeatures = new TreeMap<Feature, Double>(
				comparator);
		sortedFeatures.putAll(featureMap);
		List<Feature> nearestRacks = new ArrayList<Feature>();
		
		if(features.size() < number){
			throw new FeatureListTooSmallException();
		}
		
		if (!features.isEmpty()) {
			for (int i = 0; i < number || i < features.size(); i++) {

				Feature key = sortedFeatures.firstKey();

				nearestRacks.add(key);
				sortedFeatures.remove(key);
			}
		}
		return nearestRacks;
	}

	private static Map<Feature, Double> findDistances(List<Feature> features,
			LatLng target) {
		Map<Feature, Double> featureMap = new HashMap<Feature, Double>();
		for (Feature feature : features) {
			double[] coordinates = feature.getGeometry().getCoordinates();
			LatLng rackPosition = new LatLng(coordinates[1], coordinates[0]); // longitude
																				// first
			featureMap.put(feature, getDistance(rackPosition, target));
		}
		return featureMap;
	}

	private static double getDistance(LatLng a, LatLng b) {
		double verticalDifference = a.getLongitude() - b.getLongitude();
		double horizontalDifference = a.getLatitude() - b.getLatitude();
		return Math.sqrt((verticalDifference * verticalDifference)
				+ (horizontalDifference * horizontalDifference));
	}
}

class ValueComparator implements Comparator<Feature> {

	Map<Feature, Double> base;

	public ValueComparator(Map<Feature, Double> base) {
		this.base = base;
	}

	// Note: this comparator imposes orderings that are inconsistent with
	// equals.
	public int compare(Feature a, Feature b) {
		if (base.get(a) < base.get(b)) {
			return -1;
		} else if (base.get(a) > base.get(b)) {
			return 1;
		} else {
			return 0;
		}
	}
}